//
// Created by fazhehy on 2024/3/14.
//

#include "fpm383c.h"

#define VERIFY_CODE1    0x00
#define VERIFY_CODE2    0x00
#define VERIFY_CODE3    0x00
#define VERIFY_CODE4    0x00

union {
    uint8_t frame[24];
    struct {
        uint64_t head;
        uint16_t length;
        uint8_t checkSum;
        uint8_t verifyCode1;
        uint8_t verifyCode2;
        uint8_t verifyCode3;
        uint8_t verifyCode4;
        uint8_t command1;
        uint8_t command2;
        uint8_t data1;
        uint8_t data2;
        uint8_t data3;
        uint8_t data4;
        uint8_t data5;
        uint8_t data6;
        uint8_t data7;
    };
}sendCommand;

union {
    uint8_t frame[28];
    struct {
        uint64_t head;
        uint16_t length;
        uint8_t  checkSum;
        uint8_t verifyCode1;
        uint8_t verifyCode2;
        uint8_t verifyCode3;
        uint8_t verifyCode4;
        uint8_t command1;
        uint8_t command2;
        uint8_t errorCode1;
        uint8_t errorCode2;
        uint8_t errorCode3;
        uint8_t errorCode4;
        uint8_t data1;
        uint8_t data2;
        uint8_t data3;
        uint8_t data4;
        uint8_t data5;
        uint8_t data6;
        uint8_t data7;
    };
}receiveData;

static uint8_t get_check_sum(const uint8_t *data, uint8_t length)
{
    uint32_t i, sum = 0;
    for (i = 0; i < length; ++i) {
        sum += data[i];
    }

    return (uint8_t)(~((uint8_t)sum&0xff)+1);
}

static uint8_t data;

void fpm383c_on()
{
    FPM383C_ENABLE;
    uint8_t state = 0;
    while (state != 0x55){
        HAL_UART_Receive(&FPM383C_UART, &state, 1, FPM383C_DELAY);
    }
    sendCommand.head = 0x8aa86bb62ee21ff1;
    sendCommand.verifyCode1 = VERIFY_CODE1;
    sendCommand.verifyCode2 = VERIFY_CODE2;
    sendCommand.verifyCode3 = VERIFY_CODE3;
    sendCommand.verifyCode4 = VERIFY_CODE4;

    HAL_UART_Receive_IT(&FPM383C_UART, &data, 1);
}

void fpm383c_off()
{
    FPM383C_DISABLE;
}

static bool receiveIsFinished = false;
enum State{
    error, doing, success
};
static enum State protocol_state = error;
void fpm383c_callback()
{
    static uint8_t temp;
    static uint8_t index = 0;

    if (protocol_state == error)
        index = 0;

    receiveData.frame[index] = data;
    if (index == 10 + receiveData.length){
        if (data != get_check_sum(receiveData.frame + 11, receiveData.length - 1))
            protocol_state = error;
        else
            protocol_state = success;
    }
    else{
        switch (index ++) {
            case 0:
                if (data != 0xf1)
                    protocol_state = error;
                else
                    protocol_state = doing;
                break;
            case 1:
                if (data != 0x1f)
                    protocol_state = error;
                break;
            case 2:
                if (data != 0xe2)
                    protocol_state = error;
                break;
            case 3:
                if (data != 0x2e)
                    protocol_state = error;
                break;
            case 4:
                if (data != 0xb6)
                    protocol_state = error;
                break;
            case 5:
                if (data != 0x6b)
                    protocol_state = error;
                break;
            case 6:
                if (data != 0xa8)
                    protocol_state = error;
                break;
            case 7:
                if (data != 0x8a)
                    protocol_state = error;
                break;
            case 8:
                temp = data;
                break;
            case 9:
                receiveData.length = (temp<<8)|data;
                break;
            case 10:
                if (data != get_check_sum(receiveData.frame, 10))
                    protocol_state = error;
                break;
            case 11:
                if (data != VERIFY_CODE1)
                    protocol_state = error;
                break;
            case 12:
                if (data != VERIFY_CODE2)
                    protocol_state = error;
                break;
            case 13:
                if (data != VERIFY_CODE3)
                    protocol_state = error;
                break;
            case 14:
                if (data != VERIFY_CODE4)
                    protocol_state = error;
                break;
            default:
                break;
        }
    }

    if (protocol_state == success){
        receiveIsFinished = true;
        protocol_state = error;
        index = 0;
    }

    HAL_UART_Receive_IT(&FPM383C_UART, &data, 1);
}

static uint8_t receive_is_over()
{
    uint32_t timeout = 0;
    while (timeout <= FPM383C_DELAY){
        if (receiveIsFinished){
            receiveIsFinished = false;
            return 0;
        }
        HAL_Delay(1);
        timeout ++;
    }
    return 0xff;
}

static void send_command(uint16_t length)
{
    uint16_t temp;
    protocol_state = error;
    length += 7;
    temp = (length&0x00ff)<<8;
    temp |= (length&0xff00)>>8;

    sendCommand.length = temp;
    sendCommand.checkSum = get_check_sum(sendCommand.frame, 10);
    sendCommand.frame[10 + length] = get_check_sum(sendCommand.frame + 11, length - 1);

    HAL_UART_Transmit_DMA(&FPM383C_UART, sendCommand.frame, 11 + length);
}

/*----------------------------------------------------------------------------------------------------------------*/

uint8_t fpm383c_register_fingerprint(uint8_t reg_idx)
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x11;
    sendCommand.data1 = reg_idx;
    send_command(1);

    if (receive_is_over() == 0xff)
        return 0xff;

    return receiveData.errorCode4;
}

uint8_t fpm383c_register_fingerprint_query(uint16_t * id, uint8_t * proc)
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x12;
    send_command(0);

    if (receive_is_over() == 0xff)
        return 0xff;
    if (receiveData.errorCode4 == 0x00){
        *id = (receiveData.data1<<8)|receiveData.data2;
        *proc = receiveData.data3;
    }
    return receiveData.errorCode4;
}

uint8_t fpm383c_check_finger(uint8_t * state)
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x35;
    send_command(0);

    if (receive_is_over() == 0xff)
        return 0xff;
    if (receiveData.errorCode4 == 0x00)
        *state = receiveData.data1;
    return receiveData.errorCode4;
}

uint8_t fpm383c_save_fingerprint(uint16_t id)
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x13;
    sendCommand.data1 = (id>>8)&0xff;
    sendCommand.data2 = id&0xff;
    send_command(2);
    if (receive_is_over() == 0xff)
        return 0xff;
    return receiveData.errorCode4;
}

uint8_t fpm383c_save_fingerprint_query()
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x14;
    send_command(0);
    if (receive_is_over() == 0xff)
        return 0xff;
    return receiveData.errorCode4;
}

uint8_t fpm383c_get_fingerprint_num(uint8_t * num)
{
    sendCommand.command1 = 0x02;
    sendCommand.command2 = 0x03;
    send_command(0);
    if (receive_is_over() == 0xff)
        return 0xff;
    if (receiveData.errorCode4 == 0x00)
        *num = receiveData.data2;
    return receiveData.errorCode4;
}

uint8_t fpm383c_clear_all()
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x36;
    sendCommand.data1 = 0x01;
    sendCommand.data2 = 0x00;
    sendCommand.data3 = 0x01;
    send_command(3);
    if (receive_is_over() == 0xff)
        return 0xff;
    return receiveData.errorCode4;
}

uint8_t fpm383c_control_led(fpm383c_led_color color)
{
    sendCommand.command1 = 0x02;
    sendCommand.command2 = 0x0f;
    sendCommand.data1 = 0x04;
    sendCommand.data2 = color;
    sendCommand.data3 = 0x01;
    sendCommand.data4 = 0x00;
    sendCommand.data5 = 0x14;
    send_command(5);
    if (receive_is_over() == 0xff)
        return 0xff;
    return receiveData.errorCode4;
}

uint8_t fpm383c_match_fingerprint()
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x21;
    send_command(0);
    if (receive_is_over() == 0xff)
        return 0xff;
    return receiveData.errorCode4;
}

uint8_t fpm383c_match_fingerprint_query(uint16_t * id)
{
    sendCommand.command1 = 0x01;
    sendCommand.command2 = 0x22;
    send_command(0);
    if (receive_is_over() == 0xff)
        return 0xff;
    if (receiveData.errorCode4 == 0x00 && receiveData.data2 == 0x01){
        *id = (receiveData.data5<<8)|receiveData.data6;
        return 0;
    }
    return receiveData.errorCode4;
}

uint8_t fpm383c_sleep()
{
    sendCommand.command1 = 0x02;
    sendCommand.command2 = 0x0c;
    sendCommand.data1 = 0x00;
    send_command(1);
    if (receive_is_over() == 0xff)
        return 0xff;
    return receiveData.errorCode4;
}
